Skip to content

Conversation

@JoviDeCroock
Copy link
Contributor

@JoviDeCroock JoviDeCroock commented Jan 11, 2026

🎯 Changes

This adds the preact-ui package, we are using preact-md which I created mainly for this which already embeds the sanitize module.

I need to figure out why the types of unified here aren't compatible. We do have a lighter variant of preact-markdown in /lite but this is more akin to the solid and react ones.

Summary by CodeRabbit

  • New Features

    • Added a new ai-preact-ui package exposing Chat, ChatMessages, ChatMessage, ChatInput, ToolApproval and related UI parts
    • Supports thinking blocks, tool-call/tool-result flows with approval, and automatic message scrolling
    • Renders Markdown with syntax highlighting; components are customizable via render-props and props
  • Documentation

    • Added comprehensive README and changelog for the new package (installation, examples, APIs)

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 11, 2026

📝 Walkthrough

Walkthrough

This PR adds a new @tanstack/ai-preact-ui package with Preact UI components for chat: Chat context provider, ChatMessages, ChatMessage, ChatInput, ToolApproval, TextPart, ThinkingPart, plus docs, build/test config, and package metadata.

Changes

Cohort / File(s) Summary
Package metadata & docs
packages/typescript/ai-preact-ui/CHANGELOG.md, packages/typescript/ai-preact-ui/README.md, packages/typescript/ai-preact-ui/package.json, packages/typescript/ai-preact-ui/tsconfig.json, packages/typescript/ai-preact-ui/vite.config.ts
New package manifest, changelog, comprehensive README, TypeScript config tailored for Preact JSX, and Vite/Vitest configuration.
Public API barrel
packages/typescript/ai-preact-ui/src/index.ts
New exports aggregating components, hooks, and types for the package.
Chat plumbing & context
packages/typescript/ai-preact-ui/src/chat.tsx
Adds Chat component, ChatContext provider, and useChatContext hook to surface useChat state/actions.
Message list rendering
packages/typescript/ai-preact-ui/src/chat-messages.tsx
ChatMessages component with loading/empty/error handling, optional render prop, and automatic auto-scroll behavior.
Message rendering & parts
packages/typescript/ai-preact-ui/src/chat-message.tsx, packages/typescript/ai-preact-ui/src/text-part.tsx, packages/typescript/ai-preact-ui/src/thinking-part.tsx
ChatMessage iterates parts and delegates to renderers; TextPart renders Markdown with GFM + syntax highlighting; ThinkingPart provides collapsible thinking UI.
Input & approvals
packages/typescript/ai-preact-ui/src/chat-input.tsx, packages/typescript/ai-preact-ui/src/tool-approval.tsx
ChatInput controlled input with submit behavior and render-prop support; ToolApproval component for handling approve/deny flows and reporting via chat context.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ChatInput
    participant ChatContext as Chat
    participant Backend
    participant ChatMessages
    participant ChatMessage

    User->>ChatInput: Enter text & submit
    ChatInput->>ChatContext: sendMessage(userMessage)
    ChatContext->>Backend: send request / stream
    Backend-->>ChatContext: stream response chunks
    ChatContext->>ChatMessages: update messages state
    ChatMessages->>ChatMessage: render new message(s)
    ChatMessage->>ChatMessage: render parts (Text/Thinking/Tool)
    ChatMessages-->>User: updated conversation UI
Loading
sequenceDiagram
    participant ChatMessage
    participant TextPart
    participant ThinkingPart
    participant ToolApproval
    participant CustomRenderer as Custom Renderer

    ChatMessage->>ChatMessage: iterate message.parts
    alt part.type == "text"
        ChatMessage->>CustomRenderer: call textPartRenderer? 
        alt custom renderer provided
            CustomRenderer-->>ChatMessage: rendered node
        else
            ChatMessage->>TextPart: render Markdown
            TextPart-->>ChatMessage: rendered node
        end
    else part.type == "thinking"
        ChatMessage->>ThinkingPart: render collapsible thinking block
        ThinkingPart-->>ChatMessage: rendered node
    else part.type == "tool-call"
        ChatMessage->>ToolApproval: render approval UI if needed
        ToolApproval-->>ChatMessage: approval outcome/UI
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly related PRs

Suggested reviewers

  • AlemTuzlak
  • jherr

Poem

🐰 Hooray — new UI for chats to play,

Messages hop and thoughts relay.
Inputs, approvals, parts that gleam,
A Preact meadow for AI dreams.
Hops and code — cheers all the way!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description check ❓ Inconclusive The PR description is partially filled but missing key sections from the template: the checklist items are not addressed, and the release impact section is not completed. Complete the PR description by checking off the required checklist items and specifying release impact (whether a changeset was generated for this new package).
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'Add preact-ui package' accurately and concisely describes the main change: introducing a new preact-ui package to the repository.
Docstring Coverage ✅ Passed Docstring coverage is 88.89% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nx-cloud
Copy link

nx-cloud bot commented Jan 11, 2026

View your CI Pipeline Execution ↗ for commit ef0d14f

Command Status Duration Result
nx affected --targets=test:sherif,test:knip,tes... ✅ Succeeded 2m 56s View ↗
nx run-many --targets=build --exclude=examples/** ✅ Succeeded 2s View ↗

☁️ Nx Cloud last updated this comment at 2026-01-14 17:16:30 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 11, 2026

Open in StackBlitz

@tanstack/ai

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai@219

@tanstack/ai-anthropic

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-anthropic@219

@tanstack/ai-client

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-client@219

@tanstack/ai-devtools-core

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-devtools-core@219

@tanstack/ai-gemini

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-gemini@219

@tanstack/ai-grok

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-grok@219

@tanstack/ai-ollama

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-ollama@219

@tanstack/ai-openai

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openai@219

@tanstack/ai-preact

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-preact@219

@tanstack/ai-preact-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-preact-ui@219

@tanstack/ai-react

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react@219

@tanstack/ai-react-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react-ui@219

@tanstack/ai-solid

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-solid@219

@tanstack/ai-solid-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-solid-ui@219

@tanstack/ai-svelte

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-svelte@219

@tanstack/ai-vue

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vue@219

@tanstack/ai-vue-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vue-ui@219

@tanstack/preact-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/preact-ai-devtools@219

@tanstack/react-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/react-ai-devtools@219

@tanstack/solid-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/solid-ai-devtools@219

commit: ef0d14f

@JoviDeCroock
Copy link
Contributor Author

Fixing plugin types in JoviDeCroock/preact-markdown#2

@JoviDeCroock JoviDeCroock marked this pull request as ready for review January 14, 2026 17:03
@JoviDeCroock JoviDeCroock requested a review from a team January 14, 2026 17:03
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In `@packages/typescript/ai-preact-ui/README.md`:
- Around line 8-16: The badges in README.md point to `@tanstack/ai` instead of
this package; update the npm and bundlephobia badge links and image sources to
reference `@tanstack/ai-preact-ui` (replace occurrences of "@tanstack/ai" with
"@tanstack/ai-preact-ui" in the first and third <a> blocks), leaving the GitHub
stars badge as-is if it should still target the repo; ensure href and the src
badge URLs both use the corrected package name so badges show the right metrics.
- Line 2: The README references a missing image and lacks alt text; add the
image file at packages/typescript/ai-preact-ui/media/header_ai.png and update
the README's <img src="./media/header_ai.png"> tag to include descriptive alt
text (e.g., alt="AI Preact UI header") so the file exists and the image tag
includes accessibility text.

In `@packages/typescript/ai-preact-ui/src/chat.tsx`:
- Line 52: ChatProps declares a tools?: Record<string, any> prop that the Chat
component doesn't destructure or forward to useChat, leaving dead code; either
remove the tools field from ChatProps and any related types/usages, or
destructure tools in the Chat component (e.g. function Chat({..., tools}:
ChatProps)) and pass it into useChat (e.g. useChat({ ..., tools })) so the prop
is consumed—update tests/types accordingly and ensure any runtime behavior
expecting tools is preserved.

In `@packages/typescript/ai-preact-ui/src/text-part.tsx`:
- Around line 65-71: The implementation currently sets roleClassName to '' for
system messages causing a mismatch with the JSDoc; change the conditional that
computes roleClassName (the role === 'user' ? userClassName : role ===
'assistant' ? assistantClassName : '') so that the fallback for the 'system'
role uses assistantClassName (i.e., treat role === 'system' same as
'assistant'), or if the intended behavior is to keep an empty class for system,
update the JSDoc that describes assistantClassName to remove the "also used for
system messages" claim; adjust either the roleClassName logic or the JSDoc
accordingly so they match.
🧹 Nitpick comments (8)
packages/typescript/ai-preact-ui/README.md (1)

12-25: Replace hard tabs with spaces.

Static analysis flagged hard tabs on lines 12 and 23-25. Consider using spaces for consistent formatting.

packages/typescript/ai-preact-ui/vite.config.ts (1)

10-12: Consider using jsdom or happy-dom environment for UI component tests.

This Preact UI component library currently has no tests, but when you add component tests that render or interact with the DOM, you'll need a DOM-emulating environment like jsdom or happy-dom instead of 'node'.

Proposed change when tests are added
     globals: true,
-    environment: 'node',
+    environment: 'jsdom',
     include: ['tests/**/*.test.ts'],
packages/typescript/ai-preact-ui/src/chat-messages.tsx (1)

53-66: Consider handling error state even when errorState is not provided.

When an error occurs but no errorState renderer is provided, the component silently falls through to render messages (or empty/loading states). This could be confusing if errors go unnoticed.

Consider either:

  1. Rendering a default error UI when error exists but errorState is undefined
  2. Documenting this as expected behavior (error ignored unless errorState provided)
packages/typescript/ai-preact-ui/src/chat-message.tsx (3)

5-12: Consider stronger typing for approval and output.

Using any for these fields loses type safety benefits. If the shape is known from @tanstack/ai-client or @tanstack/ai-preact, consider importing and using those types.

Example improvement
 export interface ToolCallRenderProps {
   id: string
   name: string
   arguments: string
   state: string
-  approval?: any
-  output?: any
+  approval?: { approved?: boolean } // or import from `@tanstack/ai-client`
+  output?: unknown
 }

198-201: Redundant optional chaining.

After the truthy check on line 199, the optional chaining on line 200 is unnecessary.

Suggested fix
     // Check if there's a specific renderer for this tool
     if (toolsRenderer?.[part.name]) {
-      return <>{toolsRenderer[part.name]?.(toolProps)}</>
+      return <>{toolsRenderer[part.name](toolProps)}</>
     }

149-157: Type part more specifically to improve type safety.

Using any for part loses the benefit of type narrowing in the conditional blocks. Consider importing the MessagePart type from @tanstack/ai-client.

Suggested improvement
+import type { MessagePart } from '@tanstack/ai-client'
 import type { UIMessage } from '@tanstack/ai-preact'
 
 function MessagePart({
   part,
   // ...
 }: {
-  part: any
+  part: MessagePart
   // ...
 })
packages/typescript/ai-preact-ui/src/tool-approval.tsx (1)

53-54: Clarify why toolCallId is accepted but ignored.

The toolCallId prop is destructured and explicitly assigned to _, indicating it's intentionally unused. However, this may confuse consumers who expect it to be used. Consider either:

  1. Removing it from props if it's not needed
  2. Adding a comment explaining why it's accepted but not used (e.g., for API consistency)
  3. Using it for the approval response if that's the intended behavior
 export function ToolApproval({
-  toolCallId: _,
+  // toolCallId is accepted for API consistency but approval.id is used for responses
+  toolCallId: _toolCallId,
   toolName,
packages/typescript/ai-preact-ui/src/chat-input.tsx (1)

104-117: Consider renaming data-chat-textarea attribute.

The attribute data-chat-textarea is applied to an <input> element, which is misleading. Consider renaming to data-chat-input for consistency.

         placeholder={placeholder}
         disabled={disabled}
-        data-chat-textarea
+        data-chat-input
         style={{
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0e37d8b and 06f6f70.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (13)
  • packages/typescript/ai-preact-ui/CHANGELOG.md
  • packages/typescript/ai-preact-ui/README.md
  • packages/typescript/ai-preact-ui/package.json
  • packages/typescript/ai-preact-ui/src/chat-input.tsx
  • packages/typescript/ai-preact-ui/src/chat-message.tsx
  • packages/typescript/ai-preact-ui/src/chat-messages.tsx
  • packages/typescript/ai-preact-ui/src/chat.tsx
  • packages/typescript/ai-preact-ui/src/index.ts
  • packages/typescript/ai-preact-ui/src/text-part.tsx
  • packages/typescript/ai-preact-ui/src/thinking-part.tsx
  • packages/typescript/ai-preact-ui/src/tool-approval.tsx
  • packages/typescript/ai-preact-ui/tsconfig.json
  • packages/typescript/ai-preact-ui/vite.config.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use tree-shakeable adapter architecture for provider implementations - export specialized adapters (text, embedding, summarize, image) as separate imports from /adapters subpath rather than monolithic adapters
Use Zod for runtime schema validation and type inference, particularly for tool input/output definitions with toolDefinition() and Zod schema inference
Implement isomorphic tool system using toolDefinition() with .server() and .client() implementations for dual-environment execution
Use type-safe per-model configuration with provider options typed based on selected model to ensure compile-time safety
Implement stream processing with StreamProcessor for handling chunked responses and support partial JSON parsing for streaming AI responses

Files:

  • packages/typescript/ai-preact-ui/src/text-part.tsx
  • packages/typescript/ai-preact-ui/src/thinking-part.tsx
  • packages/typescript/ai-preact-ui/src/chat-message.tsx
  • packages/typescript/ai-preact-ui/src/chat-messages.tsx
  • packages/typescript/ai-preact-ui/src/tool-approval.tsx
  • packages/typescript/ai-preact-ui/src/chat-input.tsx
  • packages/typescript/ai-preact-ui/src/index.ts
  • packages/typescript/ai-preact-ui/src/chat.tsx
  • packages/typescript/ai-preact-ui/vite.config.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use camelCase for function and variable names throughout the codebase

Files:

  • packages/typescript/ai-preact-ui/src/text-part.tsx
  • packages/typescript/ai-preact-ui/src/thinking-part.tsx
  • packages/typescript/ai-preact-ui/src/chat-message.tsx
  • packages/typescript/ai-preact-ui/src/chat-messages.tsx
  • packages/typescript/ai-preact-ui/src/tool-approval.tsx
  • packages/typescript/ai-preact-ui/src/chat-input.tsx
  • packages/typescript/ai-preact-ui/src/index.ts
  • packages/typescript/ai-preact-ui/src/chat.tsx
  • packages/typescript/ai-preact-ui/vite.config.ts
packages/typescript/*/src/index.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Export tree-shakeable adapters with clear subpath exports in package.json (e.g., @tanstack/ai/adapters, @tanstack/ai-openai/adapters) to minimize bundle size

Files:

  • packages/typescript/ai-preact-ui/src/index.ts
🧠 Learnings (12)
📓 Common learnings
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Implement framework integrations using the headless `tanstack/ai-client` for state management with framework-specific hooks (useChat) on top
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/index.ts : Export tree-shakeable adapters with clear subpath exports in package.json (e.g., `tanstack/ai/adapters`, `tanstack/ai-openai/adapters`) to minimize bundle size

Applied to files:

  • packages/typescript/ai-preact-ui/README.md
  • packages/typescript/ai-preact-ui/tsconfig.json
  • packages/typescript/ai-preact-ui/CHANGELOG.md
  • packages/typescript/ai-preact-ui/package.json
  • packages/typescript/ai-preact-ui/src/index.ts
  • packages/typescript/ai-preact-ui/vite.config.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Implement framework integrations using the headless `tanstack/ai-client` for state management with framework-specific hooks (useChat) on top

Applied to files:

  • packages/typescript/ai-preact-ui/README.md
  • packages/typescript/ai-preact-ui/CHANGELOG.md
  • packages/typescript/ai-preact-ui/package.json
  • packages/typescript/ai-preact-ui/src/index.ts
  • packages/typescript/ai-preact-ui/src/chat.tsx
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/adapters/*.ts : Create individual adapter implementations for each provider capability (text, embed, summarize, image) with separate exports to enable tree-shaking

Applied to files:

  • packages/typescript/ai-preact-ui/tsconfig.json
  • packages/typescript/ai-preact-ui/src/index.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/model-meta.ts : Maintain model metadata files that define provider options and capabilities per model for per-model type safety

Applied to files:

  • packages/typescript/ai-preact-ui/tsconfig.json
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.{ts,tsx} : Use tree-shakeable adapter architecture for provider implementations - export specialized adapters (text, embedding, summarize, image) as separate imports from `/adapters` subpath rather than monolithic adapters

Applied to files:

  • packages/typescript/ai-preact-ui/tsconfig.json
  • packages/typescript/ai-preact-ui/src/index.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.{ts,tsx} : Use Zod for runtime schema validation and type inference, particularly for tool input/output definitions with `toolDefinition()` and Zod schema inference

Applied to files:

  • packages/typescript/ai-preact-ui/tsconfig.json
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to .prettierrc* : Use Prettier for code formatting with project-configured rules

Applied to files:

  • packages/typescript/ai-preact-ui/tsconfig.json
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to .eslintrc* : Use ESLint with custom TanStack config for linting all TypeScript and JavaScript files

Applied to files:

  • packages/typescript/ai-preact-ui/tsconfig.json
  • packages/typescript/ai-preact-ui/vite.config.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.{ts,tsx} : Implement isomorphic tool system using `toolDefinition()` with `.server()` and `.client()` implementations for dual-environment execution

Applied to files:

  • packages/typescript/ai-preact-ui/src/tool-approval.tsx
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/*/package.json : Use `workspace:*` protocol for internal package dependencies in package.json (e.g., `"tanstack/ai": "workspace:*"`)

Applied to files:

  • packages/typescript/ai-preact-ui/package.json
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.test.ts : Write unit tests using Vitest alongside source files with `.test.ts` naming convention

Applied to files:

  • packages/typescript/ai-preact-ui/vite.config.ts
🧬 Code graph analysis (6)
packages/typescript/ai-preact-ui/src/text-part.tsx (1)
packages/typescript/ai-preact-ui/src/index.ts (3)
  • TextPartProps (45-45)
  • TextPart (45-45)
  • TextPart (57-57)
packages/typescript/ai-preact-ui/src/thinking-part.tsx (2)
packages/typescript/ai-preact-ui/src/index.ts (2)
  • ThinkingPartProps (46-46)
  • ThinkingPart (46-46)
examples/vanilla-chat/src/main.js (1)
  • div (160-160)
packages/typescript/ai-preact-ui/src/chat-message.tsx (1)
packages/typescript/ai-preact-ui/src/thinking-part.tsx (1)
  • ThinkingPart (44-83)
packages/typescript/ai-preact-ui/src/tool-approval.tsx (1)
packages/typescript/ai-preact-ui/src/chat.tsx (1)
  • useChatContext (20-28)
packages/typescript/ai-preact-ui/src/chat-input.tsx (2)
packages/typescript/ai-preact-ui/src/index.ts (4)
  • ChatInputRenderProps (38-38)
  • ChatInputProps (37-37)
  • ChatInput (36-36)
  • useChatContext (28-28)
packages/typescript/ai-preact-ui/src/chat.tsx (1)
  • useChatContext (20-28)
packages/typescript/ai-preact-ui/src/chat.tsx (2)
packages/typescript/ai-preact-ui/src/index.ts (7)
  • UseChatReturn (61-61)
  • useChatContext (28-28)
  • ChatProps (28-28)
  • ConnectionAdapter (58-58)
  • UIMessage (53-53)
  • Chat (28-28)
  • useChat (49-49)
examples/vanilla-chat/src/main.js (2)
  • message (34-34)
  • div (160-160)
🪛 LanguageTool
packages/typescript/ai-preact-ui/README.md

[style] ~206-~206: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... rounded" /> ``` ### <ToolApproval> Renders approve/deny buttons for tools that req...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

🪛 markdownlint-cli2 (0.18.1)
packages/typescript/ai-preact-ui/README.md

2-2: Images should have alternate text (alt text)

(MD045, no-alt-text)


12-12: Hard tabs
Column: 1

(MD010, no-hard-tabs)


23-23: Hard tabs
Column: 1

(MD010, no-hard-tabs)


24-24: Hard tabs
Column: 1

(MD010, no-hard-tabs)


25-25: Hard tabs
Column: 1

(MD010, no-hard-tabs)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Test
  • GitHub Check: Preview
🔇 Additional comments (24)
packages/typescript/ai-preact-ui/CHANGELOG.md (1)

1-1: LGTM!

Standard initial changelog header for a new package.

packages/typescript/ai-preact-ui/README.md (1)

36-306: Documentation content looks good.

The README provides comprehensive documentation covering features, installation, quick start, component APIs, hooks, styling with data attributes, and advanced usage patterns. The examples are clear and demonstrate the intended usage of the headless components well.

packages/typescript/ai-preact-ui/tsconfig.json (1)

1-15: LGTM!

The TypeScript configuration is well-structured for a Preact library:

  • Correct JSX configuration with react-jsx and jsxImportSource: "preact" for the new JSX transform
  • Proper declaration and declarationMap generation for type definitions
  • Composite mode enabled for monorepo project references
packages/typescript/ai-preact-ui/vite.config.ts (1)

1-36: Configuration structure looks good.

The Vite/Vitest configuration properly integrates with the TanStack build tooling, uses the package name for test identification, and configures appropriate coverage settings.

packages/typescript/ai-preact-ui/package.json (3)

1-56: Package structure follows TanStack conventions.

The package manifest correctly uses:

  • workspace:* protocol for internal dependencies (per coding guidelines)
  • ESM-only exports matching the Vite config (cjs: false)
  • Proper repository metadata and keywords
  • Standard script names for the monorepo build system

37-47: No changes needed—this pattern is consistent across framework UI packages in the monorepo.

The dual listing of @tanstack/ai-preact in both dependencies and peerDependencies matches the pattern used in other framework UI packages (ai-react-ui, ai-solid-ui, etc.), indicating this is an intentional design choice to provide a fallback while allowing consumers to supply their own version.


39-42: Dependencies are compatible with each other and the unified ecosystem. Both preact-md (which depends on unified ^11.0.5) and remark-gfm (which depends on unified ^11.0.0) resolve to [email protected] correctly. The rehype-* plugins do not depend on unified and work with the hast AST. No compatibility issues found.

packages/typescript/ai-preact-ui/src/thinking-part.tsx (1)

44-83: LGTM! Well-structured collapsible component with good accessibility.

The component correctly implements auto-collapse behavior via useEffect, includes proper aria-expanded and aria-label attributes for accessibility, and the conditional rendering pattern is idiomatic.

packages/typescript/ai-preact-ui/src/text-part.tsx (1)

73-83: Good markdown rendering setup.

The combination of rehype-raw for HTML support, rehype-highlight for syntax highlighting, and remark-gfm for GitHub Flavored Markdown provides a comprehensive rendering solution. Per the PR description, preact-md includes sanitization.

packages/typescript/ai-preact-ui/src/index.ts (1)

1-61: Well-organized barrel exports with clear structure.

The exports are logically grouped (components, hooks, types) and the aliasing of TextPart as TextPartType correctly avoids naming collision with the component export. The re-exports from @tanstack/ai-preact and @tanstack/ai-client provide good DX for consumers. Based on learnings, tree-shakeable adapter patterns apply to provider implementations rather than UI component packages, so this barrel approach is appropriate here.

packages/typescript/ai-preact-ui/src/chat-messages.tsx (2)

46-51: Auto-scroll implementation is correct.

The effect properly triggers on messages changes and the immediate scroll (vs smooth) is appropriate for streaming chat scenarios where frequent updates occur.


68-85: Clean message rendering with proper keys.

Using message.id as the key ensures stable DOM reconciliation. The fallback to default ChatMessage when no custom renderer is provided is a good ergonomic choice.

packages/typescript/ai-preact-ui/src/chat-message.tsx (1)

94-139: Well-designed component with flexible rendering options.

The ChatMessage component provides a good balance of defaults and customization through the various renderer props. The isThinkingComplete calculation correctly determines if subsequent text exists, and the part iteration with proper key generation works well.

packages/typescript/ai-preact-ui/src/chat.tsx (3)

1-9: LGTM!

Clean imports with proper separation of runtime imports and type imports. Good use of the @tanstack/ai-preact package for the headless state management, following the framework integration pattern.


11-28: LGTM!

Well-implemented context pattern with appropriate error handling. The error message clearly guides developers on correct usage.


66-96: LGTM!

The Chat component correctly composes useChat with the provided props and wraps children in a context provider. The data-chat-root attribute enables styling/testing hooks.

packages/typescript/ai-preact-ui/src/tool-approval.tsx (3)

1-3: LGTM!

Clean imports with proper type separation.


63-75: LGTM!

The approval handlers correctly use approval.id to dispatch responses via addToolApprovalResponse. The hasResponded derivation correctly handles both true and false approval states.


88-128: LGTM!

Well-structured rendering logic with:

  • Render prop pattern taking precedence
  • Clear status display after response
  • Informative pending state with tool name and serialized input
  • Semantic data attributes for styling/testing
packages/typescript/ai-preact-ui/src/chat-input.tsx (5)

1-3: LGTM!

Clean imports with proper type separation.


5-31: LGTM!

Well-defined interfaces with clear JSDoc comments. The render prop pattern provides good flexibility for custom implementations.


59-76: LGTM!

Solid implementation with proper state management. The handleSubmit correctly validates input and disabled state before sending.


109-113: Shift+Enter behavior documented but not implemented.

The comment at line 29 and JSDoc at line 38 mention "Shift+Enter for new line", but the onKeyDown handler doesn't check for the Shift key. Since this is an <input> (not textarea), multi-line isn't possible anyway, but if you intend to support it in the future, the handler should be updated.

If Shift+Enter support is intended, consider:

         onKeyDown={(e) => {
-          if (submitOnEnter && e.key === 'Enter') {
+          if (submitOnEnter && e.key === 'Enter' && !e.shiftKey) {
             e.preventDefault()
             handleSubmit()
           }
         }}

138-169: LGTM!

The submit button correctly handles disabled states and provides visual feedback via hover effects. Loading state is clearly communicated to users.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@@ -0,0 +1,306 @@
<div align="center">
<img src="./media/header_ai.png" >
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if the media file exists
echo "=== Checking for media file ==="
fd -t f 'header_ai.png' packages/typescript/ai-preact-ui/ 2>&1 || echo "File not found"

# Also check directory structure
echo -e "\n=== Directory structure ==="
ls -la packages/typescript/ai-preact-ui/media/ 2>&1 || echo "Media directory not found"

# Verify the README.md content at line 2
echo -e "\n=== README.md content around line 2 ==="
sed -n '1,5p' packages/typescript/ai-preact-ui/README.md 2>&1

Repository: TanStack/ai

Length of output: 342


Image file is missing and alt text is not provided.

The image file ./media/header_ai.png does not exist in the repository, which breaks the documentation rendering. Additionally, the image tag is missing alt text for accessibility.

Proposed fix
  1. Add the missing header image file to packages/typescript/ai-preact-ui/media/header_ai.png
  2. Update the image tag to include alt text:
-  <img src="./media/header_ai.png" >
+  <img src="./media/header_ai.png" alt="TanStack AI Preact UI" >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<img src="./media/header_ai.png" >
<img src="./media/header_ai.png" alt="TanStack AI Preact UI" >
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

2-2: Images should have alternate text (alt text)

(MD045, no-alt-text)

🤖 Prompt for AI Agents
In `@packages/typescript/ai-preact-ui/README.md` at line 2, The README references
a missing image and lacks alt text; add the image file at
packages/typescript/ai-preact-ui/media/header_ai.png and update the README's
<img src="./media/header_ai.png"> tag to include descriptive alt text (e.g.,
alt="AI Preact UI header") so the file exists and the image tag includes
accessibility text.

Comment on lines +36 to +39
* Features:
* - Auto-growing textarea
* - Submit on Enter (Shift+Enter for new line)
* - Loading state management
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Documentation claims features not implemented.

The JSDoc mentions "Auto-growing textarea" and "Submit on Enter (Shift+Enter for new line)", but the implementation uses a single-line <input type="text"> (line 104-106) which:

  1. Does not auto-grow
  2. Does not support multi-line input or Shift+Enter behavior

Either update the documentation to match the current implementation, or implement a textarea with the described features.

Proposed documentation fix
 /**
  * Chat input component - handles message input and submission
  *
  * Features:
- * - Auto-growing textarea
- * - Submit on Enter (Shift+Enter for new line)
+ * - Single-line text input
+ * - Submit on Enter (configurable via submitOnEnter prop)
  * - Loading state management
  * - Full render prop support for custom UIs
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
* Features:
* - Auto-growing textarea
* - Submit on Enter (Shift+Enter for new line)
* - Loading state management
/**
* Chat input component - handles message input and submission
*
* Features:
* - Single-line text input
* - Submit on Enter (configurable via submitOnEnter prop)
* - Loading state management
* - Full render prop support for custom UIs
*/

/** Callback when an error occurs */
onError?: (error: Error) => void
/** Custom tool components registry */
tools?: Record<string, any>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Unused tools prop declared but not passed to useChat.

The tools prop is defined in ChatProps but is not destructured in the Chat component nor passed to useChat. This appears to be dead code or an incomplete feature.

Proposed fix

Either remove the unused prop:

 export interface ChatProps {
   // ... other props
   /** Callback when an error occurs */
   onError?: (error: Error) => void
-  /** Custom tool components registry */
-  tools?: Record<string, any>
 }

Or pass it to useChat if it's a supported option:

 export function Chat({
   children,
   className,
   connection,
   initialMessages,
   id,
   body,
   onResponse,
   onChunk,
   onFinish,
   onError,
+  tools,
 }: ChatProps) {
   const chat = useChat({
     connection,
     initialMessages,
     id,
     body,
     onResponse,
     onChunk,
     onFinish,
     onError,
+    tools,
   })
🤖 Prompt for AI Agents
In `@packages/typescript/ai-preact-ui/src/chat.tsx` at line 52, ChatProps declares
a tools?: Record<string, any> prop that the Chat component doesn't destructure
or forward to useChat, leaving dead code; either remove the tools field from
ChatProps and any related types/usages, or destructure tools in the Chat
component (e.g. function Chat({..., tools}: ChatProps)) and pass it into useChat
(e.g. useChat({ ..., tools })) so the prop is consumed—update tests/types
accordingly and ensure any runtime behavior expecting tools is preserved.

Comment on lines +65 to +71
const roleClassName =
role === 'user'
? userClassName
: role === 'assistant'
? assistantClassName
: ''
const combinedClassName = [className, roleClassName].filter(Boolean).join(' ')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Documentation/implementation mismatch for system messages.

The JSDoc on line 15-16 states assistantClassName is "also used for system messages," but the implementation assigns an empty string to system role instead of assistantClassName.

Either update the implementation to match the docs:

Option 1: Apply assistantClassName to system role
   const roleClassName =
     role === 'user'
       ? userClassName
-      : role === 'assistant'
-        ? assistantClassName
-        : ''
+      : role === 'assistant' || role === 'system'
+        ? assistantClassName
+        : ''

Or update the JSDoc comment if the current behavior is intended.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const roleClassName =
role === 'user'
? userClassName
: role === 'assistant'
? assistantClassName
: ''
const combinedClassName = [className, roleClassName].filter(Boolean).join(' ')
const roleClassName =
role === 'user'
? userClassName
: role === 'assistant' || role === 'system'
? assistantClassName
: ''
const combinedClassName = [className, roleClassName].filter(Boolean).join(' ')
🤖 Prompt for AI Agents
In `@packages/typescript/ai-preact-ui/src/text-part.tsx` around lines 65 - 71, The
implementation currently sets roleClassName to '' for system messages causing a
mismatch with the JSDoc; change the conditional that computes roleClassName (the
role === 'user' ? userClassName : role === 'assistant' ? assistantClassName :
'') so that the fallback for the 'system' role uses assistantClassName (i.e.,
treat role === 'system' same as 'assistant'), or if the intended behavior is to
keep an empty class for system, update the JSDoc that describes
assistantClassName to remove the "also used for system messages" claim; adjust
either the roleClassName logic or the JSDoc accordingly so they match.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/typescript/ai-preact-ui/README.md`:
- Line 12: The img tag has two alt attributes causing invalid HTML; remove the
duplicate empty alt and keep a single meaningful alt (e.g., alt="GitHub stars")
on the <img> element so it only has one alt attribute and conveys the intended
accessible text.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 06f6f70 and ef0d14f.

📒 Files selected for processing (1)
  • packages/typescript/ai-preact-ui/README.md
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Implement framework integrations using the headless `tanstack/ai-client` for state management with framework-specific hooks (useChat) on top
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/index.ts : Export tree-shakeable adapters with clear subpath exports in package.json (e.g., `tanstack/ai/adapters`, `tanstack/ai-openai/adapters`) to minimize bundle size

Applied to files:

  • packages/typescript/ai-preact-ui/README.md
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Implement framework integrations using the headless `tanstack/ai-client` for state management with framework-specific hooks (useChat) on top

Applied to files:

  • packages/typescript/ai-preact-ui/README.md
🪛 LanguageTool
packages/typescript/ai-preact-ui/README.md

[style] ~206-~206: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... rounded" /> ``` ### <ToolApproval> Renders approve/deny buttons for tools that req...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

🪛 markdownlint-cli2 (0.18.1)
packages/typescript/ai-preact-ui/README.md

2-2: Images should have alternate text (alt text)

(MD045, no-alt-text)


12-12: Hard tabs
Column: 1

(MD010, no-hard-tabs)


23-23: Hard tabs
Column: 1

(MD010, no-hard-tabs)


24-24: Hard tabs
Column: 1

(MD010, no-hard-tabs)


25-25: Hard tabs
Column: 1

(MD010, no-hard-tabs)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

<img alt="" src="https://img.shields.io/npm/dm/@tanstack/ai.svg" />
</a>
<a href="https://github.com/TanStack/ai" target="\_parent">
<img alt="" src="https://img.shields.io/github/stars/TanStack/ai.svg?style=social&label=Star" alt="GitHub stars" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix duplicate alt attribute.

The <img> tag contains two alt attributes (alt="" and alt="GitHub stars"), which is invalid HTML. Browsers typically use the first occurrence and ignore the second.

Proposed fix
-	  <img alt="" src="https://img.shields.io/github/stars/TanStack/ai.svg?style=social&label=Star" alt="GitHub stars" />
+	  <img alt="GitHub stars" src="https://img.shields.io/github/stars/TanStack/ai.svg?style=social&label=Star" />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<img alt="" src="https://img.shields.io/github/stars/TanStack/ai.svg?style=social&label=Star" alt="GitHub stars" />
<img alt="GitHub stars" src="https://img.shields.io/github/stars/TanStack/ai.svg?style=social&label=Star" />
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

12-12: Hard tabs
Column: 1

(MD010, no-hard-tabs)

🤖 Prompt for AI Agents
In `@packages/typescript/ai-preact-ui/README.md` at line 12, The img tag has two
alt attributes causing invalid HTML; remove the duplicate empty alt and keep a
single meaningful alt (e.g., alt="GitHub stars") on the <img> element so it only
has one alt attribute and conveys the intended accessible text.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant